#include "Avalon/Serialization/JsonSerializer.hpp"
#include <boost/lexical_cast.hpp>
#pragma warning(disable : 4003)
#include <libjson.h>
#pragma warning(default : 4003)

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Serialization;
using namespace boost;
using namespace libjson;
using namespace std;

struct JsonSerializer::Implementation {
  bool m_prettyPrint;
  JSONNode* m_current;
  deque<JSONNode*> m_values;

  Implementation(bool prettyPrint)
    : m_prettyPrint(prettyPrint),
      m_current(new JSONNode(JSON_NODE)) {}

  ~Implementation() {
    delete m_current;
  }

  template<typename T>
  void SerializeEntry(const char* name, T value) {
    JSONNode node;
    node = value;
    if(name != NULL) {
      node.set_name(name);
    }
    m_current->push_back(node);
  }

  void SerializeEntry(const char* name, long long value) {
    JSONNode node;
    node = lexical_cast<string>(value);
    node.cast(JSON_NUMBER);
    if(name != NULL) {
      node.set_name(name);
    }
    m_current->push_back(node);
  }

  void SerializeEntry(const char* name, unsigned long long value) {
    JSONNode node;
    node = lexical_cast<string>(value);
    node.cast(JSON_NUMBER);
    if(name != NULL) {
      node.set_name(name);
    }
    m_current->push_back(node);
  }
};

JsonSerializer::JsonSerializer(bool prettyPrint)
    : m_imp(new Implementation(prettyPrint)) {}

JsonSerializer::~JsonSerializer() {}

Buffer JsonSerializer::GetSerializedData() {
  Buffer buffer;
  string document;
  if(m_imp->m_prettyPrint) {
    document = m_imp->m_current->write_formatted();
  } else {
    document = m_imp->m_current->write();
  }
  buffer.Append(document.c_str(), document.size());
  delete m_imp->m_current;
  m_imp->m_current = new JSONNode(JSON_NODE);
  return buffer;
}

void JsonSerializer::Shuttle(const char* name, bool& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, char& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, unsigned char& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, short& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, unsigned short& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, int& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, unsigned int& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, long& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, unsigned long& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, long long& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, unsigned long long& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, float& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, double& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, string& value) {
  m_imp->SerializeEntry(name, value);
}

void JsonSerializer::Shuttle(const char* name, Buffer& data) {
  JSONNode node(JSON_STRING);
  if(name != NULL) {
    node.set_name(name);
  }
  node.set_binary(reinterpret_cast<const unsigned char*>(data.GetData()),
    data.GetSize());
  m_imp->m_current->push_back(node);
}

void JsonSerializer::StartStructure(const char* name) {
  m_imp->m_values.push_front(m_imp->m_current);
  m_imp->m_current = new JSONNode(JSON_NODE);
  if(name != NULL) {
    m_imp->m_current->set_name(name);
  }
}

void JsonSerializer::EndStructure() {
  JSONNode* object = m_imp->m_current;
  m_imp->m_current = m_imp->m_values.front();
  m_imp->m_values.pop_front();
  m_imp->m_current->push_back(*object);
  delete object;
}

void JsonSerializer::StartSequence(const char* name) {
  m_imp->m_values.push_front(m_imp->m_current);
  m_imp->m_current = new JSONNode(JSON_ARRAY);
  if(name != NULL) {
    m_imp->m_current->set_name(name);
  }
}

void JsonSerializer::EndSequence() {
  EndStructure();
}
